/*
 * @(#)AbstractAction.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.framework.web;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.util.Hashtable;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

import com.dcivision.framework.ApplicationContainer;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.ApplicationExceptionList;
import com.dcivision.framework.ApplicationMessageList;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.EventLogger;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.TextUtility;
import com.dcivision.framework.bean.SysFunction;
import com.dcivision.framework.dao.AbstractDAObject;

/**
  AbstractAction.java

  This class is to basic action class functions.

    @author          Rollo Chan
    @company         DCIVision Limited
    @creation date   26/06/2003
    @version         $Revision: 1.37.2.1 $
*/

public abstract class AbstractAction extends Action {
  protected Log log = LogFactory.getLog(this.getClass().getName());
  protected boolean isWorkflowFlag = false;
  protected String functionCode = null;

  public AbstractAction() {
    super();
  }

  /**
   * getConnection
   *
   * @param request  The HTTP request
   * @return     The connection which pre-opened and stored in request
   */
  public Connection getConnection(HttpServletRequest request) {
    return((Connection)request.getAttribute(GlobalConstant.DB_KEY));
  }

  /**
   * commit
   *
   * @param request  The HTTP request
   * @throws ApplicationException
   */
  protected void commit(HttpServletRequest request) throws ApplicationException {
    try {
      Connection conn = getConnection(request);
      conn.commit();
    } catch (Exception e) {
      log.error("Error Commit Transaction", e);
      throw new ApplicationException(ErrorConstant.DB_COMMIT_ERROR, e);
    }
  }

  /**
   * rollback
   *
   * @param request  The HTTP request
   * @throws ApplicationException
   */
  protected void rollback(HttpServletRequest request) throws ApplicationException {
    try {
      Connection conn = getConnection(request);
      conn.rollback();
    } catch (Exception e) {
      log.error("Error Rollback Transaction", e);
      throw new ApplicationException(ErrorConstant.DB_ROLLBACK_ERROR, e);
    }
  }

  /**
   * getSessionContainer
   *
   * @param request  The HTTP request
   * @return     The Session Container
   */
  public SessionContainer getSessionContainer(HttpServletRequest request) {
    SessionContainer ctx = (SessionContainer)request.getSession().getAttribute(GlobalConstant.SESSION_CONTAINER_KEY);
    if (ctx==null) {
      ctx = new SessionContainer();
      request.getSession().setAttribute(GlobalConstant.SESSION_CONTAINER_KEY, ctx);
    }
    return(ctx);
  }

  /**
   * addError
   *
   * @param request  The HTTP request
   * @param error  The Action Error
   */
  protected void addError(HttpServletRequest request, ActionError error) {
    ActionErrors errors  = (ActionErrors)request.getAttribute(Globals.ERROR_KEY);
    if (errors == null) {
      errors = new ActionErrors();
    }
    errors.add(ActionErrors.GLOBAL_ERROR, error);
    super.saveErrors(request, errors);
  }

  /**
   * addError
   *
   * @param request  The HTTP request
   * @param key    The error message key
   */
  protected void addError(HttpServletRequest request, String key) {
    this.addError(request, new ActionError(key));
  }

  /**
   * addError
   *
   * @param request  The HTTP request
   * @param key    The error message key
   * @param msg1   The error message argument 1
   */
  protected void addError(HttpServletRequest request, String key, Object msg1) {
    this.addError(request, new ActionError(key, msg1));
  }

  /**
   * addError
   *
   * @param request  The HTTP request
   * @param key    The error message key
   * @param msg1   The error message argument 1
   * @param msg2   The error message argument 2
   */
  protected void addError(HttpServletRequest request, String key, Object msg1, Object msg2) {
    this.addError(request, new ActionError(key, msg1, msg2));
  }

  /**
   * addError
   *
   * @param request  The HTTP request
   * @param key    The error message key
   * @param msg1   The error message argument 1
   * @param msg2   The error message argument 2
   * @param msg3   The error message argument 3
   */
  protected void addError(HttpServletRequest request, String key, Object msg1, Object msg2, Object msg3) {
    this.addError(request, new ActionError(key, msg1, msg2, msg3));
  }

  /**
   * addMessage
   *
   * @param request  The HTTP request
   * @param message  The Action Message
   */
  protected void addMessage(HttpServletRequest request, ActionMessage message) {
    ActionMessages messages  = (ActionMessages)request.getAttribute(Globals.MESSAGE_KEY);
    if (messages == null) {
      messages = new ActionMessages();
    }
    messages.add(ActionMessages.GLOBAL_MESSAGE, message);
    super.saveMessages(request, messages);
  }

  /**
   * addMessage
   *
   * @param request  The HTTP request
   * @param key    The message key
   */
  protected void addMessage(HttpServletRequest request, String key) {
    this.addMessage(request, new ActionMessage(key));
  }

  /**
   * addMessage
   *
   * @param request  The HTTP request
   * @param key    The message key
   * @param msg1   The message argument 1
   */
  protected void addMessage(HttpServletRequest request, String key, Object msg1) {
    this.addMessage(request, new ActionMessage(key, msg1));
  }

  /**
   * addMessage
   *
   * @param request  The HTTP request
   * @param key    The message key
   * @param msg1   The message argument 1
   * @param msg2   The message argument 2
   */
  protected void addMessage(HttpServletRequest request, String key, Object msg1, Object msg2) {
    this.addMessage(request, new ActionMessage(key, msg1, msg2));
  }

  /**
   * addMessage
   *
   * @param request  The HTTP request
   * @param key    The message key
   * @param msg1   The message argument 1
   * @param msg2   The message argument 2
   * @param msg2   The message argument 3
   */
  protected void addMessage(HttpServletRequest request, String key, Object msg1, Object msg2, Object msg3) {
    this.addMessage(request, new ActionMessage(key, msg1, msg2, msg3));
  }
/**
 * add messages
 * @param request
 * @param actionMessageList
 */
  protected void addMessages(HttpServletRequest request,ApplicationMessageList actionMessageList){
    for(Iterator it=actionMessageList.getActionMessageList().iterator();it.hasNext();){
      ActionMessage message = (ActionMessage)it.next();
      this.addMessage(request, message);
    }
  }
  /**
   * isError
   *
   * @param request  The HTTP request
   * @return     Whether error has been occurred before
   */
  protected boolean isError(HttpServletRequest request) {
    return(request.getAttribute(Globals.ERROR_KEY)!=null && !((ActionErrors)request.getAttribute(Globals.ERROR_KEY)).isEmpty());
  }

  /**
   * getDAObjectByClassName
   *
   * @param request       The HTTP request
   * @param sClassName    The data access object class name
   * @return              The data access object created by the class name
   * @throws ApplicationException
   */
  protected AbstractDAObject getDAObjectByClassName(HttpServletRequest request, String sClassName) throws ApplicationException {
    SessionContainer sessionCon = this.getSessionContainer(request);
    Connection conn = this.getConnection(request);
    Class clsDAO = null;
    Constructor conDAO = null;
    AbstractDAObject dao = null;

    try {
      clsDAO = Class.forName(sClassName);
      conDAO = clsDAO.getConstructor(new Class[]{SessionContainer.class, Connection.class});
      dao = (AbstractDAObject)conDAO.newInstance(new Object[] { sessionCon, conn });
      return(dao);
    } catch (java.lang.reflect.InvocationTargetException ite) {
      log.error("Error when initializing DAO", ite);
      throw new ApplicationException(ErrorConstant.COMMON_UNKNOWN_DAO, ite, this.getMajorDAOClassName());
    } catch (Exception e) {
      log.error("Error when initializing DAO", e);
      throw new ApplicationException(ErrorConstant.COMMON_UNKNOWN_DAO, e, this.getMajorDAOClassName());
    }
  }

  /**
   * getMajorDAObject
   *
   * @param request       The HTTP request
   * @return              The major DAO used in this class
   * @throws ApplicationException
   */
  protected AbstractDAObject getMajorDAObject(HttpServletRequest request) throws ApplicationException {
    return(this.getDAObjectByClassName(request, this.getMajorDAOClassName()));
  }

  /**
   * retrieveFunctionCode
   *
   * Retrieve the function code of this class and set it in the HTTP request for
   * further process.
   *
   * @param request       The HTTP request
   */
  protected ActionForward retrieveFunctionCode(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
    String funcCode = this.functionCode == null ? this.getFunctionCode() : this.functionCode;    
    String[] funcCodes = { funcCode };    
    String condition=null;    
    if(funcCode!=null){
      if(funcCode.indexOf("||")>0){
        condition="OR";
        funcCodes = TextUtility.splitString(funcCode,"||");
      }else if(funcCode.indexOf("&&")>0){
        condition="AND";
        funcCodes = TextUtility.splitString(funcCode,"&&");
      }        
      request.setAttribute(GlobalConstant.FUNCTION_CODE_KEY, funcCodes[0]);
    }            
    
    SessionContainer sessionCon = this.getSessionContainer(request);

    // Special handling for SHOW HEADER AND FOOTER flag.
    if (request.getParameter("SHOW_HEADER_FOOTER") != null) {
      request.setAttribute("SHOW_HEADER_FOOTER", request.getParameter("SHOW_HEADER_FOOTER"));
    }
    if(funcCode!=null){
      try {
        ApplicationContainer appContainer = (ApplicationContainer)request.getSession().getServletContext().getAttribute(GlobalConstant.APPLICATION_CONTAINER_KEY);
        int noAccessRightCount=0;      
        for(int i=0;i<funcCodes.length;i++){      
          try{
            SysFunction sysFunc = appContainer.getSysFunctionByFunctionCode(funcCodes[i]);
            if (sysFunc != null && (request.getRequestURL()==null || request.getRequestURL().indexOf("user/PopupUserRecord.do")<0)) {
              sessionCon.getPermissionManager().isAccessible(GlobalConstant.OBJECT_TYPE_FUNCTION, sysFunc.getID());
            }
          } catch (ApplicationException e) {
            if (ErrorConstant.COMMON_UNAUTHORIZED_ACCESS.equals(e.getMsgCode())) {            
              noAccessRightCount++;
            }else{
              throw e;
            }
          }          
        }    
        if(noAccessRightCount>0){
       
          if(condition==null || 
              "AND".equals(condition) || 
             ("OR".equals(condition) && noAccessRightCount==funcCodes.length)
             )
          {
            String queryString = request.getQueryString();
            if (queryString!=null && queryString.indexOf("isLogin=Y")>=0) {
              return mapping.findForward("Home0");
            } else {
              if (sessionCon.getUserRecordID()!=null) {
                handleApplicationException(request, new ApplicationException(ErrorConstant.COMMON_UNAUTHORIZED_ACCESS));
                return mapping.findForward("Home");
              } else {
                handleApplicationException(request, new ApplicationException(ErrorConstant.COMMON_UNAUTHORIZED_ACCESS));
                return mapping.findForward("Login");
              }
            }
          }
        }
      } catch (ApplicationException e) {      
        if (ErrorConstant.LOGIN_SESSION_EXPIRED.equals(e.getMsgCode())) {
          handleApplicationException(request, e);
          return mapping.findForward("Login");
        }
      }  
    }      
    this.getSessionContainer(request).saveLastUsedFunction(funcCode);

    // Log down which function the current login user is using.
    try {
      log.info("User Record ID: " + this.getSessionContainer(request).getUserRecordID() + "; funcCode: " + funcCode);
      this.getSessionContainer(request).getAppContainer().updateUsingFunction(this.getSessionContainer(request).getUserRecordID(), funcCode);
    } catch (Exception e) {
      log.error("Cannot Log the Function Code.", e);
    }

    return null;
  }

  /**
   * handleApplicationException
   *
   * @param request       The HTTP request
   * @param appEx         The Application Exception or Application Exception List.
   */
  protected void handleApplicationException(HttpServletRequest request, ApplicationException appEx) {
    if (appEx instanceof ApplicationExceptionList) {
      ApplicationException[] appExAry = ((ApplicationExceptionList)appEx).getExceptionArray();
      for (int i = 0; i < appExAry.length; i++) {
        addError(request, appExAry[i].getActionError());
      }
    } else {
      addError(request, appEx.getActionError());
    }
    //event code    
    if (TextUtility.parseInteger(SystemParameterFactory.getSystemParameter(SystemParameterConstant.SYSTEMLOG_EVENTLOG))>0){
      String functionName = "";
      if (this.functionCode != null){
        // EIP-438 06/12/28 LEE / some page have not function code, and this.functionCode not equals function.getFunctionName.
        SysFunction function = this.getSessionContainer(request).getAppContainer().getSysFunctionByFunctionCode(getMainFunctionCode());
        if (function!=null) functionName = function.getFunctionName();
      }
      StringWriter strWriter = new StringWriter();
      PrintWriter prtWriter = new PrintWriter(strWriter);
      appEx.printStackTrace(prtWriter);
      String errorDetail = strWriter.toString();
      Hashtable eventCodehash = (Hashtable)request.getSession(false).getServletContext().getAttribute(GlobalConstant.EVENT_CODE_KEY);
      boolean flag = false;
      String msgCode = appEx.getMsgCode();
      if(eventCodehash!=null && msgCode!=null){
        flag = eventCodehash.containsKey(msgCode);
        if (flag){
          EventLogger.log(request,msgCode,functionName,errorDetail);
        }
      }
    }     
  }

  public String getMainFunctionCode(){
    String funcCode = getFunctionCode();
    String[] funcCodes = {""};
    if(funcCode!=null){
      if(funcCode.indexOf("||")>0){
        //condition="OR";
        funcCodes = TextUtility.splitString(funcCode,"||");
      }else if(funcCode.indexOf("&&")>0){
        //condition="AND";
        funcCodes = TextUtility.splitString(funcCode,"&&");
      }else{
        funcCodes[0] = funcCode;
      }      
    }  
    return funcCodes[0];
  }

  /**
   * setFunctionCode - Set to override the "getFunctionCode()".
   *
   * @param code   The function code
   */
  public void setFunctionCode(String code) {
    this.functionCode = code;
  }

  /**
   * getFunctionCode
   *
   * Abstract function which sub-class should implement to return the corresponding
   * function code.
   *
   * @return   The function code
   */
  public abstract String getFunctionCode();

  /**
   * getMajorDAOClassName
   *
   * Abstract function which sub-class should implement to return the corresponding
   * major DAO class name used in this class.
   *
   * @return   The DAO class name
   */
  public abstract String getMajorDAOClassName();
  
  /**
   * setPageTitle
   * 
   * empty method which sub-class can implement to set the page Path and page Title
   * @param actionForward TODO
   * @throws ApplicationException 
   * @throws NumberFormatException 
   *
   */
  public  void setPageTitle(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping, ActionForward actionForward) throws NumberFormatException, ApplicationException{
   /**
    * request.setAttribute(GlobalConstant.EXTEND_PAGE_PATH,'');
    * request.setAttribute(GlobalConstant.EXTEND_PAGE_TITLE,'');
    * request.setAttribute(GlobalConstant.EXTEND_PAGE_TITLE_SHOW_ONLY,true);
    */ 
  }

}
